import 'dart:math';

main() {
  // requestSomething();
  // requestSomething2();
  requestWithWait();
}

// todo 异步支持
//Dart类库有非常多的返回Future或者Stream对象的函数。 这些函数被称为异步函数：它们只会在设置好一些耗时操作之后返回，比如像IO操作。而不是等到这个操作完成。
//async和await关键词支持了异步编程，允许您写出和同步代码很像的异步代码

//todo 1. Future,表示一个异步操作的最终完成（或失败）及其结果值的表示,一个Future只会对应一个结果，要么成功，要么失败。
//Future 的所有API的返回值仍然是一个Future对象，所以可以很方便的进行链式调用。

//todo 1.1）Future.then then中接收异步结果
requestSomething() {
  Future<String>.delayed(const Duration(seconds: 2), () {
    return "you got something."; //2秒后返回结果字符串
  }).then((data) {
    print(data); //在then中接收异步结果并打印结果
  });
}

//todo 1.2）Future.catchError,如果异步任务发生错误，我们可以在catchError中捕获错误
requestSomething2() {
  Future<String>.delayed(const Duration(seconds: 2), () {
    throw AssertionError("error");
  }).then((data) {
    print("success:$data");
  }).catchError((e) {
    print("error:$e");
  });
}

//then方法还有一个可选参数onError,也可以用它来捕获异常：
requestSomething3() {
  Future<String>.delayed(const Duration(seconds: 2), () {
    throw AssertionError("error");
  }).then((data) {
    print("success:$data");
  }, onError: (e) {
    print("error:$e");
  });
}

//todo 1.3）Future.whenComplete,在请求结束后执行
requestWhenComplete() {
  Future<String>.delayed(Duration(seconds: 2), () {
    //执行异步请求
    throw AssertionError("error");
  }).then((data) {
    //执行成功回调
  }).catchError((e) {
    //执行失败回调
  }).whenComplete(() {
    //执行完成回调
    print("whenComplete");
  });
}

//todo 1.4）Future.wait,等待多个异步任务都执行结束后才进行一些操作,它接受一个Future数组参数，只有数组中所有Future都执行成功后，才会触发then的成功回调，只要有一个Future执行失败，就会触发错误回调
requestWithWait() {
  Future.wait([
    //2s后返回结果
    Future.delayed(Duration(seconds: 2), () {
      return "hello";
    }),
    //4s后返回结果
    Future.delayed(Duration(seconds: 4), () {
      return "你好";
    })
  ]).then((results) {
    //执行成功回调
    print(results[0] + results[1]);
  }).catchError((e) {
    print("请求失败：$e");
  });
}

//todo 2. async/await 异步任务串行化
//现在有个需求场景是用户先登录，登录成功后会获得用户ID，然后通过用户ID，再去请求用户个人信息，获取到用户个人信息后，为了使用方便，我们需要将其缓存在本地文件系统:
//先分别定义各个异步任务：

// todo 2.1）回调地狱(Callback Hell)
/// 用户登录
Future<String> login(String username, String pwd) {
  return Future.delayed(const Duration(seconds: 2), () {
    return "ok";
  });
}

/// 获取用户信息
Future<String> getUserInfo(String userId) {
  return Future.delayed(const Duration(seconds: 3), () {
    return "userInfo";
  });
}

/// 缓存用户信息
Future cacheUserInfo(String userInfo) {
  return Future.delayed(const Duration(seconds: 2), () {
    print("缓存用户信息：$userInfo");
  });
}

///执行整个任务流：
runTasks() {
  login("username", "pwd").then((data) {
    getUserInfo("userId").then((result) {
      cacheUserInfo(result).then((result) {});
    });
  });
}

//todo 2.2）消除回调地狱
//一、使用Future消除Callback Hell
runTasks2() {
  login("username", "pwd")
      .then((id) {
        return getUserInfo(id);
      })
      .then((userInfo) {
        return cacheUserInfo(userInfo);
      })
      .then((reult) {})
      .catchError((onError) {});
}

// todo 二、使用 async/await 消除 callback hell
//async用来表示函数是异步的，定义的函数会返回一个Future对象，可以使用 then 方法添加回调函数。
//await 后面是一个Future，表示等待该异步任务完成，异步完成后才会往下走；await必须出现在 async 函数内部。
runTasks3() async{
  try{
    String id = await login("username", "pwd");
    String userInfo = await getUserInfo(id);
    await cacheUserInfo(userInfo);
    //执行接下来的操作
  } catch(e){
    print("error:$e");
  }
}
